/*****************************************************************************
* | File      	:	LCD_2IN_Driver.c
* | Author      :   Waveshare team
* | Function    :   LCD driver
* | Info        :
*----------------
* |	This version:   V1.0
* | Date        :   2018-12-18
* | Info        :
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to  whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
******************************************************************************/
#include "LCD.h"
#include <stdio.h>
#include <stdlib.h> //itoa()
#include <string.h>

#include <wiringPi.h>
#include <wiringPiSPI.h>

#include <unistd.h>
/*******************************************************************************
function:
        Hardware reset
*******************************************************************************/
static void LCD_Reset(void)
{
    digitalWrite(LCD_CS, 1);
    delay(100);
    digitalWrite(LCD_RST, 0);
    delay(100);
    digitalWrite(LCD_RST, 1);
    delay(100);
}

/*******************************************************************************
function:
                Write data and commands
*******************************************************************************/
static void LCD_Write_Command(unsigned char data)
{
    digitalWrite(LCD_CS, 0);
    digitalWrite(LCD_DC, 0);
    wiringPiSPIDataRW(0, &data, 1);
}

static void LCD_WriteData_Byte(unsigned char data)
{
    digitalWrite(LCD_CS, 0);
    digitalWrite(LCD_DC, 1);
    wiringPiSPIDataRW(0, &data, 1);
    digitalWrite(LCD_CS, 1);
}

void LCD_WriteData_Word(unsigned short data)
{
    digitalWrite(LCD_CS, 0);
    digitalWrite(LCD_DC, 1);
    unsigned char data1 = (data >> 8) && 0xff;
    unsigned char data2 = data;
    wiringPiSPIDataRW(0, &data1, 1);
    wiringPiSPIDataRW(0, &data2, 1);
    digitalWrite(LCD_CS, 1);
}

/******************************************************************************
function:
                Common register initialization
******************************************************************************/
void LCD_Init(void)
{
    LCD_Reset();

    LCD_Write_Command(0x36);
    LCD_WriteData_Byte(0x00);

    LCD_Write_Command(0x3A);
    LCD_WriteData_Byte(0x05);

    LCD_Write_Command(0x21);

    LCD_Write_Command(0x2A);
    LCD_WriteData_Byte(0x00);
    LCD_WriteData_Byte(0x00);
    LCD_WriteData_Byte(0x01);
    LCD_WriteData_Byte(0x3F);

    LCD_Write_Command(0x2B);
    LCD_WriteData_Byte(0x00);
    LCD_WriteData_Byte(0x00);
    LCD_WriteData_Byte(0x00);
    LCD_WriteData_Byte(0xEF);

    LCD_Write_Command(0xB2);
    LCD_WriteData_Byte(0x0C);
    LCD_WriteData_Byte(0x0C);
    LCD_WriteData_Byte(0x00);
    LCD_WriteData_Byte(0x33);
    LCD_WriteData_Byte(0x33);

    LCD_Write_Command(0xB7);
    LCD_WriteData_Byte(0x35);

    LCD_Write_Command(0xBB);
    LCD_WriteData_Byte(0x1F);

    LCD_Write_Command(0xC0);
    LCD_WriteData_Byte(0x2C);

    LCD_Write_Command(0xC2);
    LCD_WriteData_Byte(0x01);

    LCD_Write_Command(0xC3);
    LCD_WriteData_Byte(0x12);

    LCD_Write_Command(0xC4);
    LCD_WriteData_Byte(0x20);

    LCD_Write_Command(0xC6);
    LCD_WriteData_Byte(0x0F);

    LCD_Write_Command(0xD0);
    LCD_WriteData_Byte(0xA4);
    LCD_WriteData_Byte(0xA1);

    LCD_Write_Command(0xE0);
    LCD_WriteData_Byte(0xD0);
    LCD_WriteData_Byte(0x08);
    LCD_WriteData_Byte(0x11);
    LCD_WriteData_Byte(0x08);
    LCD_WriteData_Byte(0x0C);
    LCD_WriteData_Byte(0x15);
    LCD_WriteData_Byte(0x39);
    LCD_WriteData_Byte(0x33);
    LCD_WriteData_Byte(0x50);
    LCD_WriteData_Byte(0x36);
    LCD_WriteData_Byte(0x13);
    LCD_WriteData_Byte(0x14);
    LCD_WriteData_Byte(0x29);
    LCD_WriteData_Byte(0x2D);

    LCD_Write_Command(0xE1);
    LCD_WriteData_Byte(0xD0);
    LCD_WriteData_Byte(0x08);
    LCD_WriteData_Byte(0x10);
    LCD_WriteData_Byte(0x08);
    LCD_WriteData_Byte(0x06);
    LCD_WriteData_Byte(0x06);
    LCD_WriteData_Byte(0x39);
    LCD_WriteData_Byte(0x44);
    LCD_WriteData_Byte(0x51);
    LCD_WriteData_Byte(0x0B);
    LCD_WriteData_Byte(0x16);
    LCD_WriteData_Byte(0x14);
    LCD_WriteData_Byte(0x2F);
    LCD_WriteData_Byte(0x31);
    LCD_Write_Command(0x21);

    LCD_Write_Command(0x11);

    LCD_Write_Command(0x29);
}

/******************************************************************************
function:	Set the cursor position
parameter	:
          Xstart: 	Start unsigned short x coordinate
          Ystart:	Start unsigned short y coordinate
          Xend  :	End unsigned short coordinates
          Yend  :	End unsigned short coordinatesen
******************************************************************************/
void LCD_SetWindow(unsigned short Xstart, unsigned short Ystart, unsigned short Xend, unsigned short Yend)
{
    LCD_Write_Command(0x2a);
    LCD_WriteData_Byte(Xstart >> 8);
    LCD_WriteData_Byte(Xstart & 0xff);
    LCD_WriteData_Byte((Xend - 1) >> 8);
    LCD_WriteData_Byte((Xend - 1) & 0xff);

    LCD_Write_Command(0x2b);
    LCD_WriteData_Byte(Ystart >> 8);
    LCD_WriteData_Byte(Ystart & 0xff);
    LCD_WriteData_Byte((Yend - 1) >> 8);
    LCD_WriteData_Byte((Yend - 1) & 0xff);

    LCD_Write_Command(0x2C);
}

/******************************************************************************
function:	Settings window
parameter	:
          Xstart: 	Start unsigned short x coordinate
          Ystart:	Start unsigned short y coordinate

******************************************************************************/
void LCD_SetCursor(unsigned short X, unsigned short Y)
{
    LCD_Write_Command(0x2a);
    LCD_WriteData_Byte(X >> 8);
    LCD_WriteData_Byte(X);
    LCD_WriteData_Byte(X >> 8);
    LCD_WriteData_Byte(X);

    LCD_Write_Command(0x2b);
    LCD_WriteData_Byte(Y >> 8);
    LCD_WriteData_Byte(Y);
    LCD_WriteData_Byte(Y >> 8);
    LCD_WriteData_Byte(Y);

    LCD_Write_Command(0x2C);
}

/******************************************************************************
function:	Clear screen function, refresh the screen to a certain color
parameter	:
          Color :		The color you want to clear all the screen
******************************************************************************/
void LCD_Clear(unsigned short Color)
{
    unsigned short i;
    unsigned short image[LCD_WIDTH];
    for (i = 0; i < LCD_WIDTH; i++) {
        image[i] = Color >> 8 | (Color & 0xff) << 8;
    }
    unsigned char *p = (unsigned char *)(image);
    LCD_SetWindow(0, 0, LCD_WIDTH, LCD_HEIGHT);
    digitalWrite(LCD_DC, 1);
    for (i = 0; i < LCD_HEIGHT; i++) {
        wiringPiSPIDataRW(0, p, LCD_WIDTH * 2);
    }
}

/******************************************************************************
function:	Refresh a certain area to the same color
parameter	:
          Xstart: Start unsigned short x coordinate
          Ystart:	Start unsigned short y coordinate
          Xend  :	End unsigned short coordinates
          Yend  :	End unsigned short coordinates
          color :	Set the color
******************************************************************************/
void LCD_ClearWindow(unsigned short Xstart, unsigned short Ystart, unsigned short Xend, unsigned short Yend, unsigned short color)
{
    unsigned short i, j;
    LCD_SetWindow(Xstart, Ystart, Xend - 1, Yend - 1);
    for (i = Ystart; i <= Yend - 1; i++) {
        for (j = Xstart; j <= Xend - 1; j++) {
            LCD_WriteData_Word(color);
        }
    }
}

/******************************************************************************
function: Show a picture
parameter	:
                image: Picture buffer
******************************************************************************/
void LCD_Display(unsigned char *image)
{
    unsigned short i;
    LCD_SetWindow(0, 0, LCD_WIDTH, LCD_HEIGHT);
    digitalWrite(LCD_DC, 1);
    for (i = 0; i < LCD_HEIGHT; i++) {
        wiringPiSPIDataRW(0, image + LCD_WIDTH * 2 * i, LCD_WIDTH * 2);
    }
}

/******************************************************************************
function: Draw a point
parameter	:
            X	: 	Set the X coordinate
            Y	:	Set the Y coordinate
          Color :	Set the color
******************************************************************************/
void LCD_DrawPaint(unsigned short x, unsigned short y, unsigned short Color)
{
    LCD_SetCursor(x, y);
    LCD_WriteData_Word(Color);
}

void Handler_2IN_LCD(int signo)
{
    // System Exit
    printf("\r\nHandler:Program stop\r\n");
    exit(0);
}

void GPIO_Mode(unsigned short Pin, unsigned short Mode)
{
    if (Mode == 0 || Mode == INPUT) {
        pinMode(Pin, INPUT);
        pullUpDnControl(Pin, PUD_UP);
    } else {
        pinMode(Pin, OUTPUT);
        // printf (" %d OUT \r\n",Pin);
    }
}

void GPIO_Init(void)
{
    GPIO_Mode(LCD_CS, 1);
    GPIO_Mode(LCD_RST, 1);
    GPIO_Mode(LCD_DC, 1);
    if (getuid() == 0) {
        GPIO_Mode(LCD_BL, 1);
    }

    digitalWrite(LCD_CS, 1);
    digitalWrite(LCD_BL, 1);
}
/******************************************************************************
function:	Module Initialize, the library and initialize the pins, SPI
protocol parameter: Info:
******************************************************************************/
unsigned char DEV_ModuleInit(void)
{
    // if(wiringPiSetup() < 0)//use wiringpi Pin number table
    if (wiringPiSetupGpio() < 0) { // use BCM2835 Pin number table
        printf("set wiringPi lib failed	!!! \r\n");
        return 1;
    }
    GPIO_Init();
    wiringPiSPISetup(0, 25000000);

    // 判断是否有root权限
    if (!getuid()) {
        pinMode(LCD_BL, PWM_OUTPUT);
        pwmWrite(LCD_BL, 512);
    } else {
        printf("Root privileges are required to set the backlight\n");
    }
    return 0;
}